#include "OpenGLShaderProgram.h"
#include "OpenGLShader.h"
#include <glad/glad.h>

#include <glm/gtc/type_ptr.hpp>

namespace Fysics
{
	OpenGLShaderProgram::OpenGLShaderProgram(const Shader* vertex, const Shader* fragment)
		: ShaderProgram()
	{
		GLuint program = glCreateProgram();
		OpenGLShader* vertexShader = (OpenGLShader*) vertex;
		OpenGLShader* fragShader = (OpenGLShader*) fragment;
		glAttachShader(program, vertexShader->GetID());
		glAttachShader(program, fragShader->GetID());
		// Link our program
		glLinkProgram(program);

		GLint isLinked = 0;
		glGetProgramiv(program, GL_LINK_STATUS, &isLinked);
		if (isLinked == GL_FALSE)
		{
			GLint maxLength = 0;
			glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
			char* infoLog = new char[maxLength];
			glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
			std::cout << "Shader Program:\n" << infoLog << std::endl;
			delete[] infoLog;

			glDeleteProgram(program);

			return;
		}

		glDetachShader(program, vertexShader->GetID());
		glDetachShader(program, fragShader->GetID());

		m_RendererID = program;
	}

	OpenGLShaderProgram::~OpenGLShaderProgram()
	{
		glDeleteProgram(m_RendererID);
	}

	void OpenGLShaderProgram::Bind() const
	{
		glUseProgram(m_RendererID);
	}

	void OpenGLShaderProgram::UnBind() const
	{
		glUseProgram(0);
	}

	void OpenGLShaderProgram::ActiveTexture(unsigned int slot)
	{
		glActiveTexture(GL_TEXTURE0 + slot);
	}

	void OpenGLShaderProgram::UploadUniform(const std::string& name, const glm::mat4& mat4x4) const
	{
		GLint location = glGetUniformLocation(m_RendererID, name.c_str());
		glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(mat4x4));
	}

	void OpenGLShaderProgram::UploadUniform(const std::string& name, const glm::vec3& float3) const
	{
		GLint location = glGetUniformLocation(m_RendererID, name.c_str());
		glUniform3f(location, float3.x, float3.y, float3.z);
	}

	void OpenGLShaderProgram::UploadUniform(const std::string& name, unsigned int uint) const
	{
		GLint location = glGetUniformLocation(m_RendererID, name.c_str());
		glUniform1i(location, uint);
	}

	void OpenGLShaderProgram::UploadUniform(const std::string& name, const glm::vec4& float4) const
	{
		GLint location = glGetUniformLocation(m_RendererID, name.c_str());
		glUniform4f(location, float4.r, float4.g, float4.b, float4.a);
	}
}